package com.example.java_gobang.api;


import com.example.java_gobang.game.MatchRequest;
import com.example.java_gobang.game.MatchResponse;
import com.example.java_gobang.game.Matcher;
import com.example.java_gobang.game.OnlineUserManager;
import com.example.java_gobang.model.UserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

@Component
//通过这个类来处理WebSocket中的请求
public class MatchAPI extends TextWebSocketHandler {
    //处理JSON字符串和对象
    private ObjectMapper objectMapper = new ObjectMapper();

    //维护玩家的在线状态/离线状态
    @Autowired
    private OnlineUserManager onlineUserManager;

    //玩家匹配器
    @Autowired
    private Matcher matcher;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //玩家上线，加入到OnlineUserManager中
        //1.拿到用户的身份信息
        //用户的身份信息是在注册WebSocket时，通过addInterceptors(new HttpSessionHandshakeInterceptor());这个方法获取的
        //这个方法的逻辑是通过将HttpSession的Attribute的信息全部存入到WebSocketSession当中，此时就能够拿到HttpSession中的Attribute
        //又因为HttpSession在用户登入时，用户会将自己的信息放入到HttpSession的Attribute当中，此时就能够通过WebSocketSession进行获取用户的身份信息
        //需要注意的是，此时拿到的userInfo可能为空，当userInfo为空时(也就是用户并没有进行登入操作，HttpSession中不存在用户信息)
        //如果直接访问game_hall.html，此时的用户信息为null，因为此时HttpSession并未拿到用户的身份信息
        try {
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

            //2.先判定当前用户是否已经是在线状态了，如果已经是在线状态了，则不再进行后续的操作（解决多开问题）
            //从玩家在线管理器中获取当前玩家是否在线
            // 这个玩家管理器的方法是判断玩家是否在游戏大厅中，如果在游戏大厅中，则在线，反之，则不在线
            if (onlineUserManager.getFromGameRoom(userInfo.getId()) != null
             || onlineUserManager.getFromGameHall(userInfo.getId()) != null) {
                //此时用户已经是在线状态了
                MatchResponse matchResponse = new MatchResponse();
                //ok的这个成员变量true是用来处理服务器意料之中的情况（也就是对应不同的情况时，有对应的处理方式），意料之外的情况才是false
                matchResponse.setOk(true);
                matchResponse.setReason("用户已经在线了，不准多开!");
                matchResponse.setMessage("repeatConnection");
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(matchResponse)));
                //需要注意的是，当session进行close操作时，会直接跳转到afterConnectionClosed和handleTransportError方法中，此时这两个方法也要进行处理
                //session.close();
                return;
            }

            //3.拿到用户的身份信息之后，就可以把玩家设置成在线状态了
            onlineUserManager.enterGameHall(userInfo.getId(), session);
            System.out.println("玩家: " + userInfo.getUserName() + "进入游戏大厅！");
        }catch (NullPointerException e){
            e.printStackTrace();
            //此时出现了用户未登录的情况，需要将信息返回给客户端
            MatchResponse response = new MatchResponse();
            response.setOk(false);
            response.setReason("用户未登录");
            //使用writeValueAsString转成JSON字符串
            //使用sendMessage需要通过TextMessage对象这个文本格式的WebSocket数据报进行封装后才能传输
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //实现开始匹配请求和停止匹配请求
        UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

        //获取客户端给服务器发送的请求信息message
        //要将message中的payload数据载荷从JSON字符串转换成Java字符串
        String requst = message.getPayload();
        MatchRequest request = objectMapper.readValue(requst, MatchRequest.class);
        MatchResponse response = new MatchResponse();
        if (request.getMessage().equals("startMatch")) {
            //处理开始匹配
            //创建匹配队列，把当前用户加入匹配队列
            matcher.add(userInfo);

            //将玩家放入匹配队列当中，就需要给客户端返回一个响应
            response.setOk(true);
            response.setMessage("startMatch");
        }else if (request.getMessage().equals("stopMatch")) {
            //处理停止匹配
            //在匹配队列中，将用户移除
            matcher.exit(userInfo);
            //把玩家从匹配队列中移除，就给客户端返回一个响应

            response.setOk(true);
            response.setMessage("stopMatch");
        }else {
            //处理非法情况
            response.setOk(false);
            response.setMessage("非法情况！");
        }

        //此时还需要将message返回给客户端
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //玩家下线，从OnlineUserManager移除
        try {
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(userInfo.getId());
            if (tmpSession == session) {
                onlineUserManager.exitGameHall(userInfo.getId());
            }
            //如果玩家直接下线也需要进行退出匹配操作
            System.out.println("因为异常退出匹配");
            matcher.exit(userInfo);
        }catch (NullPointerException e){
            //e.printStackTrace();
            System.out.println("handleTransportError当前用户未登录");
            /*MatchResponse response = new MatchResponse();
            response.setOk(false);
            response.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));*/
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //玩家下线，从OnlineUserManager移除
        try {
            UserInfo userInfo = (UserInfo) session.getAttributes().get("userInfo");

            //如果进行多开操作，此时userId只会有一个key但是value的值此时存在两个，一个是用户在线状态的WebSocketSession1
            //还有一个是多开时，产生的WebSocketSession2。所以此时需要关闭WebSocketSession2，但是可能会对WebSocketSession1进行误伤
            //所以需要判断当前的WebSocketSession是否是当前需要关闭的WebSocketSession2
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(userInfo.getId());
            //当多开时，建立连接的操作中，会把此时的WebSocketSession2存储到这里

            if (session == tmpSession) {
                //判断此时WebSocketSession是否和多开的WebSocketSession2相同，相同才会进行关闭操作。
                //并且也只能关闭用户的相同的会话，而不能关闭其他用户的会话
                onlineUserManager.exitGameHall(userInfo.getId());
            }
            //如果玩家直接下线也需要进行退出匹配操作
            System.out.println("正常下线退出匹配");
            matcher.exit(userInfo);
        }catch (NullPointerException e){
            //e.printStackTrace();
            System.out.println("afterConnectionClosed当前用户未登录");
//            MatchResponse response = new MatchResponse();
//            response.setOk(false);
//            response.setReason("用户未登录");
//            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
    }
}
